/**
 * @fileOverview <p>Includes classes and interfaces for {@link eui.Table}</p> 
 * @author embrace.toolkit
 * @version $Id: table.js 106 2009-10-01 08:24:37Z embrace.toolkit $
 */

/**
 * <p>Prototype UI Extension for extending HTMLTable elements.</p>
 * 
 * <p>Class adds abilty to sort and paginate HTML tables with only one script. 
 * It`s planned to make pluggable tableColumnSorters for different formats.</p> 
 * 
 * <p>Currently only lexical sorting is possible.</p>
 * 
 * @class Prototype UI Extension for extending HTMLTable elements.
 * @public
 * @see eui.Table.Options
 * @author Embrace.toolkit
 */
eui.Table.Table = Class.create({
	/** @lends eui.Table.Table */
	/**
	 * The reference element in the DOM
	 * @private
	 * @type Element
	 */
	table: false,
	/**
	 * The page limit, how many entries are allowed per page. Could be 
	 * customized within the eui.Table.TableOptions object you give to 
	 * the constructor.
	 * @private
	 * @default 10
	 * @type Integer
	 */
	pageLimit: 10,
	/**
	 * The current selected page of the paginated table.
	 * @private
	 * @type Integer
	 */
	currentPage: 1,
	/**
	 * Detected maximum of pages in the paginated table.
	 * @private 
	 * @type Integer
	 */
	maxPages: false,
	/**
	 * reference to the used {@link eui.TableSorter}
	 * 
	 * @private
	 * @type eui.TableSorter
	 * @default new {@link eui.TableSorter}
	 */
	tableSorter: false,
	/**
	 * Default sorting direction is "asc" to change the default value
	 * please use {@link eui.Table.Options}
	 * @private
	 * @type String
	 */
	defaultSorting: "asc",
	/**
	 * The used implementation of {@link eui.TableSorter}.
	 * Its possible to write own implementations of eui.TableSorter.
	 * @private
	 * @type eui.TableSorter
	 */	
	highlighter: false,
	/**
	 * Called by constructor to create an instance of eui.Table.
	 * 
	 * Calls all setup methods:
	 * <ul>
	 *  <li>{@link eui.Table.setupData}</li>
 	 * 	<li>{@link eui.Table.setupPagination}</li>
 	 * 	<li>{@link eui.Table.setupSorting}</li>
 	 * </ul>
 	 * 
	 * @public
	 * @param eui.Table.Options options for creating a eui.table instance. For
	 * @throws {NullException} if {@link eui.Table.Options.element} is null 
	 */ 
	initialize: function(parameters) {
		this.table = $(parameters.element);
		if(!this.table) {
			throw([
				"NullException: table with id: ",
				parameters.element,
				"cannot be null."
			].join(" "));
		}
		
		if(parameters.pageLimit)
			this.pageLimit = parameters.pageLimit;
		
		if(parameters.defaultSorting)
			this.defaultSorting = parameters.defaultSorting;
		
		try {
			if(parameters.highlighter)
				this.highlighter = parameters.highlighter;
			else 
				this.highlighter = new eui.Table.Highlighter({table: this});
		}catch(exc) {
			console.log("Exc: %o", exc);
		}

		try {
			this.table.observe("data:changed", this.dataChanged.bind(this));
			this.setupData();
			this.setupPagination();
			this.setupSorting();
		}catch(exc) {
			console.error("Exception: %o", exc);
		}
	},
	/**
	 * Called when tables custom event "data:changed" is fired. You can 
	 * register own observers to the table for handling some action on the 
	 * same event.
	 * @event data:changed
	 * @param Event changeEvent - the notified changeEvent
	 * @private
	 */
	dataChanged: function(changeEvent) {
		this.setupData();
		this.setupPagination();
		this.displayPage();
		
		this.highlighter.run();
	},
	/**
	 * Initializes the sorting features of eui.Table.
	 * @private
	 */
	setupSorting: function() {
		this.table.select("th[fieldName]").first().up().stopObserving(
			"mouseover"
		);
		this.table.select("th[fieldName]").first().up().stopObserving(
			"mouseout"
		);
		
		this.table.select("th[fieldName]").each(function(th) {
			th.observe("mouseover", function() {
				this.addClassName("over"); 
			});
			th.observe("mouseout", function() { 
				this.removeClassName("over"); 
			});
			
			th.observe("click", this.sorterClicked.bind(this));
		}.bind(this));
	},
	/**
	 * Handles the "click" event of the sorting handles. Normally the sorting 
	 * handles are the th elements which contain the attribute "fieldName". 
	 * @param Event clickEvent - the notified click event. 
	 * @event click
	 * @private
	 */
	sorterClicked: function(clickEvent) {
		if(this.sorter) {
			var el = clickEvent.findElement();
			this.table.select("th[fieldName]").invoke("removeClassName", "desc");
			this.table.select("th[fieldName]").invoke("removeClassName", "asc");
			
			var sorting = !el.readAttribute("sorting") ? this.defaultSorting : el.readAttribute("sorting");
			sorting = sorting == "asc" ? "desc" : "asc";		
			el.addClassName(sorting);
			el.writeAttribute({"sorting": sorting});
			
			this.sorter.sort(
				el.readAttribute("fieldName"), 
				sorting
			);
		}
	},
	/**
	 * Initializes the pagination features of eui.Table.
	 * @private
	 */
	setupPagination: function() {
		var footer = this.table.select("#footer").first();
		footer.up().stopObserving("mouseover");
		footer.up().stopObserving("mouseout");
		
		var ul = new Element("ul").addClassName("pageLinkList");
		
		for(var i = 1; i <= this.maxPages; i++) {
			var pageLink = new Element("li", {
				pageLink: i
			});
			pageLink.addClassName("pageLink");
			pageLink.update("" + i);
			
			pageLink.observe("mouseover", this.pageLinkOver);
			pageLink.observe("mouseout", this.pageLinkOut);
			pageLink.observe("click", this.pageLinkClick.bind(this));
			
			ul.insert(pageLink);
		}
		
		footer.update(ul);
	},
	/**
	 * Handles the mouse over event if any pagination link is 
	 * notifiing its mouseOver event. 
	 * @event over
	 * @private
	 */
	pageLinkOver: function() {
		this.addClassName("over");
	},
	/**
	 * Handles the mouse over event if any pagination link is 
	 * notifiing its mouseOut event.
	 * @event out
	 * @private
	 */
	pageLinkOut: function() {
		this.removeClassName("over");
	},
	/**
	 * Handles the mouse over event if any pagination link is 
	 * notifiing its mouseClick event.
	 * @event click
	 * @param Event clickEvent - the notified event.
	 * @private
	 */
	pageLinkClick: function(clickEvent) {
		this.table.select(".pageLink").invoke("removeClassName", "active");
		
		var element = clickEvent.findElement();
		element.addClassName("active");
		this.currentPage = element.readAttribute("pageLink"); 
		this.displayPage();
	},
	/**
	 * Initializes the data and setup look and feel of eui.table.
	 * @private
	 */
	setupData: function() {
		this.hideBody();
		
		var counter = 1;
		var page = 1;
		this.table.select("tr:not(.heading)").each(function(tr) {
			tr.writeAttribute({"page": page});
			
			if(counter == this.pageLimit) {
				page++;
				counter = 0;
			}
			counter++;
		}.bind(this));		
		
		this.maxPages = page;
		this.displayPage(this.currentPage);
	},
	/**
	 * Displays {@link eui.Table.currentPage} page 
	 * of the paginated eui.table.
	 * @private
	 */
	displayPage: function() {
		this.hideBody();
		this.table.select("tr[page=" + this.currentPage + "]:not(heading)").invoke("show");
		this.table.select(".pageLink").invoke("removeClassName", "active");
		this.table.select("li[pageLink=" + this.currentPage + "]").invoke("addClassName", "active");
	},
	/**
	 * Hides all TR Elements which are not with CSS class "heading".
	 * @private
	 */
	hideBody: function() {
		this.table.select("tr:not(.heading)").invoke("hide");
	},
	/**
	 * Sets the {@link eui.Tables.sorter} to the given 
	 * {@link eui.TableSorter} implementation.
	 * @param eui.TableSorter sorter implememtation of eui.TableSorter
	 * @public
	 */
	setSorter: function(sorter) {
		if(!sorter)
			throw("NullPointerException: Sorter could not be null.");
		
		this.sorter = sorter;
	}
});

/**
 * @class The options used for creating a {@link eui.Table.Table}.
 * @example <p>This is an interface how create a eui.Table.Options
 * for creating an instance of eui.Table.Table.</p>
 * <p>You could create an eui.Table.Options instance using 
 * traditional instanciation like:
 * <code>
 * var tableOptions = new eui.Table.Options();
 * tableOptions.element = $("table");
 * tableOptions.pageLimit = 50;
 * 
 * var table = new eui.Table.Table(tableOptions);
 * </code>
 * or by using the inline method like:
 * <code>
 * var tableOptions = {
 *	element: $("table"),
 *	pageLimit: 50
 * };
 * 
 * var table = new eui.Table.Table(tableOptions);
 * </code>
 * </p>
 * @public
 * @author Embrace.toolkit
 * @interface
 */
eui.Table.Options = Class.create({
	/** @lends eui.Table.Options */
	/**
	 * The DOM reference or ID of the used HTML table.
	 * @type Element
	 * @public
	 */
	element: false,
	/**
	 * Maximum entries per page.
	 * @type Integer
	 * @default 10
	 * @public
	 */
	pageLimit: 10
});